package com.rfid.acs.net.tcp;

import android.util.Log;

import com.rfid.acs.net.ResponseWrapper;
import com.rfid.common.log.LogUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;

public class TcpClient {

    private static final int CONNECT_TIMEOUT = 10000;

    private static final int READ_TIMEOUT = 10000;

    private String TAG = "TcpClient";

    private SocketAddress address;

    private String charsetName = "UTF-8";

    private TcpClient(SocketAddress address, String deviceNo) {
        this.address = address;
    }

    public static class Builder {

        private SocketAddress address;

        private String ip;

        private int port = -1;

        private String charsetName;

        private String deviceNo = "";

        public Builder address(SocketAddress address) {
            this.address = address;
            return this;
        }

        public Builder ip(String ip) {
            this.ip = ip;
            return this;
        }

        public Builder port(int port) {
            this.port = port;
            return this;
        }

        public Builder device(String deviceNo) {
            this.deviceNo = deviceNo;
            return this;
        }

        public Builder charset(String charsetName) {
            this.charsetName = charsetName;
            return this;
        }

        public TcpClient build() {
            if (address != null) {
                return new TcpClient(address, deviceNo);
            } else {
                if (ip == null) {
                    throw new RuntimeException("address或 ip&port 必须设置");
                }

                if (port < 0) {
                    throw new RuntimeException("address或 ip&port 必须设置");
                }

                if (deviceNo == null) {
                    throw new RuntimeException("deviceNo 不能为 null");
                }

                address = new InetSocketAddress(ip, port);
                TcpClient tcpClient = new TcpClient(address, deviceNo);
                if (this.charsetName != null) {
                    tcpClient.charsetName = charsetName;
                }

                return tcpClient;
            }
        }
    }

    /**
     * 发送单条命令的请求
     *
     * @param requestBean
     * @return
     */
    public Observable<String> request(final IRequest requestBean) {
        return Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                synchronized (TcpClient.this) {
                    Log.d("NETLOG", "request start thread = " + Thread.currentThread().getName());
                    Socket socket = null;
                    OutputStream outputStream = null;
                    BufferedReader reader = null;

                    try {
                        //请求参数拼接的字符串
                        String requestMsg = requestBean.requestMessage();
                        LogUtils.v(requestMsg);
                        LogUtils.v(address);
                        //建立socket连接
                        socket = new Socket();
                        socket.connect(address, CONNECT_TIMEOUT);
                        socket.setSoTimeout(READ_TIMEOUT);
                        InputStream inputStream = socket.getInputStream();
                        outputStream = socket.getOutputStream();
                        reader = new BufferedReader(new InputStreamReader(inputStream, charsetName));

                        outputStream.write(requestMsg.getBytes(charsetName));
                        outputStream.flush();
                        String response = readFromStream(reader);
                        LogUtils.v(response);
                        Log.d("NETLOG", "request:" + requestMsg + ",response:" + response);
                        emitter.onNext(response);

                    } finally {

                        if (outputStream != null) {
                            try {
                                outputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        if (reader != null) {
                            try {
                                reader.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        if (socket != null) {
                            try {
                                socket.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    Log.d("NETLOG", "request end thread = " + Thread.currentThread().getName());
                }
            }
        });
    }

    private String readFromStream(BufferedReader reader) throws Exception {
        long startTime = System.currentTimeMillis();
        if (reader != null) {
            StringBuffer buffer = new StringBuffer();
            String line = null;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            long endTime = System.currentTimeMillis();
            Log.d("NETLOG", "耗时:" + (endTime - startTime) + "ms");
            return buffer.toString();
        }

        return null;
    }

    public Observable<List<ResponseWrapper>> requestWrapperSysc(final List<IRequest> requests) {
        return Observable.create(new ObservableOnSubscribe<List<ResponseWrapper>>() {
            @Override
            public void subscribe(ObservableEmitter<List<ResponseWrapper>> emitter) throws Exception {
                List<ResponseWrapper> wrappers = new ArrayList<>();
                for (IRequest requestBean : requests) {
                    wrappers.add(requestSync(requestBean));
                }
                emitter.onNext(wrappers);
            }
        });
    }

    public synchronized ResponseWrapper requestSync(IRequest request) throws Exception {
        Log.d("NETLOG", "requestSync start thread = " + Thread.currentThread().getName());
        Socket socket = null;
        OutputStream outputStream = null;
        BufferedReader reader = null;

        try {
            long start = System.currentTimeMillis();
            //建立socket连接
            socket = new Socket();
            socket.connect(address, CONNECT_TIMEOUT);
            long end1 = System.currentTimeMillis();
            Log.d("TIME_ACS", "END 1 TIME = " + (end1 - start));
            socket.setSoTimeout(READ_TIMEOUT);
            InputStream inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
            long end2 = System.currentTimeMillis();
            Log.d("TIME_ACS", "END 2 TIME = " + (end2 - end1));
            reader = new BufferedReader(new InputStreamReader(inputStream, charsetName));

            //请求参数拼接的字符串
            String requestMsg = request.requestMessage() + '\r';
            outputStream.write(requestMsg.getBytes(charsetName));
            outputStream.flush();
            long end3 = System.currentTimeMillis();
            Log.d("TIME_ACS", "END 3 TIME = " + (end3 - end2));
            String response = readFromStream(reader);
            long end4 = System.currentTimeMillis();
            Log.d("TIME_ACS", "END 4 TIME = " + (end4 - end3));
            Log.d("NETLOG", "socket:" + socket + "request:" + requestMsg + ",response:" + response);
            ResponseWrapper wrapper = new ResponseWrapper(request, response);
            long end = System.currentTimeMillis();
            Log.d("NETLOG", "requestSync end thread = " + Thread.currentThread().getName() + "耗时:" + (end - start) + "ms");
            return wrapper;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
